/*
 * GeneticForest.h
 *
 *  Created on: 24.06.2012
 *      Author: ignacio
 */

#ifndef GENETICFOREST_H_
#define GENETICFOREST_H_

#include "ArithmeticParser.h"
#include <iostream>
using std::cout;
using std::endl;


namespace neurignacio{
void Example();
// typedef
typedef vector<OperatorPointer> OperatorPointerContainer_t;
typedef vector<int> NumberContainer_t;
typedef list<NumberNode> NumberNodeContainer_t;
typedef list<OperatorNode> OperatorNodeContainer_t;
// GenTree
class GenTree : public Tree
{
private:
	void inorderNumberLeaf(Node* x, list<int>* l) const;
public:
	double score;
public:
	GenTree(): Tree() {}
	void print() const {Tree::print(); cout << " = " << this->eval();}
	int eval() const {return root->eval();}
	friend void swap(GenTree& X, GenTree& Y);
	void setRoot(OperatorNode* op) {root=op; root->p=Tree::nil;}
	bool hasRepetitions() const;
	list<int> getNumberLeaf() const;
//	void inorderCopy(Node* x, Node* y, GenTree* T);
};
typedef list<GenTree> GenTreeContainer_t;

class GenTreeComparison
{
public:
	int goal;
public:
	GenTreeComparison(const int goal) : goal(goal) {}
	bool operator() (const GenTree& lhs, const GenTree& rhs) const
	{
		return (fitScore(lhs.eval(), goal) > fitScore(rhs.eval(), goal));
	}
};

class Forest
{
public:
	GenTree* fittest;
	int goal;
	bool allow_repetitions;
	// const
	Addition ADDITION;
	Substraction SUBSTRACTION;
	Multiplication MULTIPLICATION;
	Division DIVISION;
	OperatorPointerContainer_t OPERATORPOINTERCONTAINER;
	NumberContainer_t NUMBERCONTAINER;
	//
	OperatorNodeContainer_t operatorNodeContainer;
	NumberNodeContainer_t numberNodeContainer;
	//
	GenTreeContainer_t treeContainer;
	//
	GenTreeComparison comparison;


private:
	static const unsigned char NUMBER_OF_OPERATIONS=4;
public:
	Forest(const NumberContainer_t& v, int goal, bool allow_repetitions=true)
		: fittest(0), goal(goal), allow_repetitions(allow_repetitions), ADDITION(), SUBSTRACTION(), MULTIPLICATION(), DIVISION(),
		  OPERATORPOINTERCONTAINER(NUMBER_OF_OPERATIONS,0), NUMBERCONTAINER(v.begin(), v.end()),
		  operatorNodeContainer(), numberNodeContainer(), treeContainer(),
		  comparison(goal)
		{init();}
	Forest(const NumberPointer numberArray, const size_t arraySize, int goal, bool allow_repetitions=true)
		: fittest(0), goal(goal), allow_repetitions(allow_repetitions), ADDITION(), SUBSTRACTION(), MULTIPLICATION(), DIVISION(),
		  OPERATORPOINTERCONTAINER(NUMBER_OF_OPERATIONS,0), NUMBERCONTAINER(numberArray,numberArray+arraySize),
		  operatorNodeContainer(), numberNodeContainer(), treeContainer(),
		  comparison(goal)
		{init();}
	void init();
	//
	GenTree& createRandomTree();
	void createDefaultTree();
	NumberNode* newRandomNumberNode();
	NumberNode* newNumberNode(const int& value);	// Create a new number node based on VALUE!
	NumberNode* newNumberNode(NumberPointer num);
	OperatorNode* newRandomOperatorNode();
	OperatorNode* newOperatorNode(const unsigned int index); // Create a new operator node based on INDEX!
	OperatorNode* newOperatorNode(OperatorPointer op);
	void initiate();
	GenTreeContainer_t::iterator RouletteWheelSelection(double totalScore, GenTreeContainer_t* container);
	double getScore(const GenTree& tree);
	double getPopulationScore();
	double getBestScore();
//	void updatePopulationScore();
	void mate();
	GenTree& duplicate(GenTree& tree);
	void print();
};

class CopyGenTree
{
	// Duplicate X in Y by value
public:
	Node* x; // Node in 'X' tree
	Node* y; // Node in 'Y' tree
	Node* yPrevious; // Node in 'Y' tree
//	Node* y2;
	GenTree* X;
	GenTree* Y;
public:
	CopyGenTree(GenTree* X, GenTree* Y): x(X->root), y(Y->root), yPrevious(Y->nil), X(X), Y(Y) {}
	void inorderCopy(Node* x, Forest* forest);
};

} // end namespace neurignacio


#endif /* GENETICFOREST_H_ */
